using System;
using System.Collections.Specialized;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace TextEditorDP
{

    public class CommandManager: Subject
    {

        private static CommandManager instance = new CommandManager();

        // store the command list:
        private CList<Command> commands = new CList<Command>();
        // store the position of the current command (just executed):
        private int current = (-1);

        private int maxCommand = 10;

        // prevent the client to create instance:
        private CommandManager() { }

        /**
         * Get the only instance of the CommandManager. 
         * 
         * @return The instance of the CommandManager.
         */
        public static CommandManager GetInstance() { return instance; }

        // add the command that just executed:
        private void AddToCommandList(Command cmd)
        {
            if (commands.Count > maxCommand)
            {
                commands.Remove(commands.Get(0));
            }

            if (cmd.CanUndo())
            {
                current++;
                commands.Add(current, cmd);
            }
        }

        /**
         * Undo the last command. 
         */
        public void Undo()
        {
            if (CanUndo())
            {
                commands.Get(current).UnExecute();
                current--;
                Notify();
            }
        }

        /**
         * Can redo the last undo command? 
         * 
         * @return True if can redo.
         */
        public bool CanRedo()
        {
            return current < commands.Count - 1;
        }

        /**
         * Redo the last cancelled command. 
         */
        public void Redo()
        {
            if (CanRedo())
            {
                this.current++;
                commands.Get(this.current).Execute();
                Notify();
            }
        }

        /**
         * Can undo the last command? 
         * 
         * @return True if can undo.
         */
        public bool CanUndo()
        {
            return current >= 0;
        }

        public void NewInsertRTFCommand(ref CRichTextBox edit, string text, Font font, RtfColor textColor, RtfColor backColor)
        {
            Command cmd = new InsertRTFCommand(ref edit, text, font, textColor, backColor);
            if (cmd.Execute() && cmd.CanUndo())
            {
                AddToCommandList(cmd);
                Notify();
            }
        }

        public void NewInsertTextCommand(ref CRichTextBox edit, string text)
        {
            Command cmd = new InsertTextCommand(ref edit, text);
            if (cmd.Execute() && cmd.CanUndo())
            {
                AddToCommandList(cmd);
                Notify();
            }
        }

        /**
         * Create a new insert picture command and execute it. 
         * 
         * @param doc The document object.
         * @param filename The picture file name.
         */
        public void NewInsertPictureCommand(ref CRichTextBox edit, Image image)
        {
            Command cmd = new InsertPictureCommand(ref edit, image);
            if (cmd.Execute() && cmd.CanUndo())
            {
                AddToCommandList(cmd);
                Notify();
            }
        }

        internal void NewCutCommand(ref CRichTextBox edit)
        {
            Command cmd = new CutCommand(ref edit); 
            if (cmd.Execute() && cmd.CanUndo())
            {
                AddToCommandList(cmd);
                Notify();
            }
        }

        internal void NewPasteCommand(ref CRichTextBox edit)
        {
            Command cmd = new PasteCommand(ref edit);
            if (cmd.Execute() && cmd.CanUndo())
            {
                AddToCommandList(cmd);
                Notify();
            }
        }

        internal void NewClearCommand(ref CRichTextBox edit)
        {
            Command cmd = new ClearCommand(ref edit);
            if (cmd.Execute() && cmd.CanUndo())
            {
                AddToCommandList(cmd);
                Notify();
            }
        }

        internal void NewSelectAllCommand(ref CRichTextBox edit)
        {
            Command cmd = new SelectAllCommand(ref edit);
            if (cmd.Execute() && cmd.CanUndo())
            {
                AddToCommandList(cmd);
                Notify();
            }
        }

        internal void NewChangeFontCommand(ref CRichTextBox edit, Font font)
        {
            Command cmd = new ChangeFontCommand(ref edit, font);
            if (cmd.Execute() && cmd.CanUndo())
            {
                AddToCommandList(cmd);
                Notify();
            }
        }

        internal void NewChangeColorCommand(ref CRichTextBox edit, Color color)
        {
            Command cmd = new ChangeColorCommand(ref edit, color);
            if (cmd.Execute() && cmd.CanUndo())
            {
                AddToCommandList(cmd);
                Notify();
            }
        }

        internal void NewChangeZoomCommand(ref CRichTextBox edit, float zoom)
        {
            Command cmd = new ChangeZoomCommand(ref edit, zoom);
            if (cmd.Execute() && cmd.CanUndo())
            {
                AddToCommandList(cmd);
                Notify();
            }
        }

        #region Subject Members
        CList<Observer> observers = new CList<Observer>();
        public void Attach(Observer observer)
        {
            observers.Add(observers.Count, observer);
        }

        public void Detach(Observer observer)
        {
            observers.Remove(observer);
        }

        public void Notify()
        {
            Iterator<Observer> iterator = observers.CreateIterator();
            while(!iterator.IsDone)
            {
                iterator.Next().Update();
            }
        }

        #endregion
    }
}
